package com.bruce.starts.demo.commonerror.collection;


import cn.hutool.core.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;


import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;

/**
 * @author luotuan
 * @Description
 * @create 2021-04-27 21:14
 **/
@Slf4j
public class CurrentHashMapDemo {
    private static final int THREAD_COUT = 10;
    private static final int COUNT = 1000;
    private static final int LOOP_COUNT = 10000000;

    private ConcurrentHashMap<String,Long> getData(int count){
        return LongStream.rangeClosed(1,count)
                .boxed()
                .collect(Collectors.toMap(i -> UUID.randomUUID().toString(), Function.identity(),(o1,o2)->o1,ConcurrentHashMap::new));
    }

    private void addTest() throws InterruptedException {
        ConcurrentHashMap<String,Long> map = getData(900);
        log.info("init size:{}",map.size());
        ForkJoinPool forkJoinPool = new ForkJoinPool(THREAD_COUT);
        forkJoinPool.execute(()-> IntStream.rangeClosed(1,10).parallel().forEach(i->{
                log.info("Thread:{}",i);
                synchronized (map){
                    int gap = COUNT - map.size();
                    log.info("gap size: {}",gap);
                    map.putAll(getData(gap));
                }
        }));
        forkJoinPool.shutdown();
        forkJoinPool.awaitTermination(1, TimeUnit.HOURS);
        log.info("finish size：{}",map.size());
    }

    private Map<String,Long> normalUseKeyCount() throws InterruptedException {
        ConcurrentHashMap<String,Long> resultMap = new ConcurrentHashMap<>(COUNT);
        ForkJoinPool forkJoinPool = new ForkJoinPool(THREAD_COUT);
        forkJoinPool.execute(()->IntStream.rangeClosed(1,LOOP_COUNT).parallel().forEach(i->{
            String key = "item"+ ThreadLocalRandom.current().nextInt(COUNT);
            synchronized (resultMap){
                if(resultMap.containsKey(key)){
                    resultMap.put(key,resultMap.get(key)+1);
                }else {
                    resultMap.put(key,1L);
                }
            }
        }));
        forkJoinPool.shutdown();
        forkJoinPool.awaitTermination(1,TimeUnit.HOURS);
        return resultMap;
    }
    private Map<String,Long> goodUseKeyCount() throws InterruptedException{
        ConcurrentHashMap<String, LongAdder> resultMap = new ConcurrentHashMap<>(COUNT);
        ForkJoinPool forkJoinPool = new ForkJoinPool(THREAD_COUT);
        forkJoinPool.execute(()->IntStream.rangeClosed(1,LOOP_COUNT).parallel().forEach(i->{
            String key = "item"+ ThreadLocalRandom.current().nextInt(COUNT);
            resultMap.computeIfAbsent(key,k->new LongAdder()).increment();
        }));
        forkJoinPool.shutdown();
        forkJoinPool.awaitTermination(1,TimeUnit.HOURS);
        return resultMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v->v.getValue().longValue()));
    }

    private void testKeyCount() throws InterruptedException {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("normalUse");
        Map<String,Long> normalUse = normalUseKeyCount();
        stopWatch.stop();
        Assert.isTrue(normalUse.size()==COUNT,"normalUse数量不对");
        Assert.isTrue(normalUse.entrySet().stream().mapToLong(Map.Entry::getValue).reduce(0,Long::sum)==LOOP_COUNT,"错误");
        stopWatch.start("goodUse");
        Map<String,Long> goodUse = goodUseKeyCount();
        stopWatch.stop();
        Assert.isTrue(goodUse.size()==COUNT,"goodUse数量不对");
        Assert.isTrue(goodUse.entrySet().stream().mapToLong(Map.Entry::getValue).reduce(0,Long::sum)==LOOP_COUNT,"错误");
        log.info(stopWatch.prettyPrint());
    }


    public static void main(String[] args) throws InterruptedException {
        CurrentHashMapDemo demo = new CurrentHashMapDemo();
        demo.testKeyCount();
    }
}
